package cn.echo.consul.manager.service.impl;

import cn.echo.consul.manager.entity.KeyValueItem;
import cn.echo.consul.manager.entity.KeyValueRecord;
import cn.echo.consul.manager.exception.BizServiceException;
import cn.echo.consul.manager.repository.ManagerRepository;
import cn.echo.consul.manager.service.KeyValueItemService;
import cn.echo.consul.manager.util.DateUtil;
import cn.echo.consul.manager.util.KeyValueUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.yaml.snakeyaml.Yaml;

import java.util.*;

import static cn.echo.consul.manager.response.ResultCode.*;


/**
 * <p>
 * Consul KV管理 服务实现类
 * </p>
 *
 * @author lonyee
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class KeyValueItemServiceImpl implements KeyValueItemService {

    final ManagerRepository managerRepository;

    @Value("${manager.consul.key-value.prefix}")
    private String keyValuePrefix;
    @Value("${manager.consul.key-value.data-key}")
    private String keyValueDataKey;
    @Value("${manager.consul.key-value.profile-separator}")
    private String profileSeparator;

    private final String profileSlash = "/";
    private final String profileDot = ".";
    private final String recordFormat = "manager/record/%s/%s::%s";
    private final String recordPrefixFormat = "manager/record/%s/";
    private final String pattern = "yyyyMMddHHmmssSSS";
    private final String yamlKey = "yamlSource";

    /**
     * 获取KeyValue配置列表
     */
    public List<KeyValueItem> getKeyValueItems(String projectId, String env) {
        String profile = this.keyValueProfile(projectId, env, profileSlash);
        List<KeyValueItem> list = new ArrayList<>();
        Map<String, Object> map = managerRepository.get(profile, Map.class);
        Map<String, String> configs = KeyValueUtil.getKVConfigs(map);
        //map -> list
        for (Map.Entry<String, String> entry: configs.entrySet()) {
            KeyValueItem dict = new KeyValueItem(entry.getKey(), entry.getValue(), "");
            list.add(dict);
        }
        return list;
    }

    /**
     * 保存KeyValue配置信息
     */
    public void modifyKeyValueItem(String userId, String projectId, String env, String key, String value) {
        if (StringUtils.isEmpty(projectId)) {
            throw new BizServiceException(F11622);
        }
        if (StringUtils.isEmpty(key)) {
            throw new BizServiceException(F11625);
        }
        if (StringUtils.isEmpty(value)) {
            throw new BizServiceException(F11626);
        }
        String profile = this.keyValueProfile(projectId, env, profileSlash);

        Map<String, Object> map = managerRepository.get(profile, Map.class);
        if (map==null) {
            map = new TreeMap<>();
        }
        String oldValue = KeyValueUtil.change(map, key, value);
        managerRepository.update(profile, map);

        //保存历史记录
        this.saveKeyValueRecord(userId, projectId, env, key, oldValue, value);
    }

    /**
     * 移除KeyValue配置信息
     */
    public void removeKeyValueItem(String userId, String projectId, String env, String key) {
        if (StringUtils.isEmpty(projectId)) {
            throw new BizServiceException(F11622);
        }
        if (StringUtils.isEmpty(key)) {
            throw new BizServiceException(F11625);
        }
        Map<String, Object> resultMap = new TreeMap<>();

        String profile = this.keyValueProfile(projectId, env, profileSlash);

        Map<String, Object> map = managerRepository.get(profile, Map.class);
        Map<String, String> configs = KeyValueUtil.getKVConfigs(map);
        Iterator<Map.Entry<String, String>> iterator = configs.entrySet().iterator();
        while(iterator.hasNext()) {
            Map.Entry<String, String> entry = iterator.next();
            if (key.equals(entry.getKey())) {
                //保存历史记录
                this.saveKeyValueRecord(userId, projectId, env, entry.getKey(), entry.getValue(), "");
                iterator.remove();
            }
        }
        for (Map.Entry<String, String> entry: configs.entrySet()) {
            KeyValueUtil.change(resultMap, entry.getKey(), entry.getValue());
        }
        managerRepository.update(profile, resultMap);
    }

    /**
     * 保存KeyValue YAML配置信息
     */
    public void modifyKeyValueYml(String userId, String projectId, String env, String yamlSource, Boolean replace) {

        if (StringUtils.isEmpty(projectId)) {
            throw new BizServiceException(F11622);
        }
        if (StringUtils.isEmpty(yamlSource)) {
            throw new BizServiceException(F11627);
        }

        Yaml yaml = new Yaml();
        Map<String, Object> configMap;
        try {
            configMap = yaml.loadAs(yamlSource, Map.class);
        } catch (Exception ex) {
            log.error("yaml格式转换失败", ex);
            throw new BizServiceException(F11628);
        }

        String profile = this.keyValueProfile(projectId, env, profileSlash);
        //替换全部
        if (replace!=null && replace) {
            if (configMap==null) {
                configMap = new TreeMap<>();
            }
            //保存历史记录
            String oldYaml = managerRepository.getYaml(profile);
            this.saveKeyValueRecord(userId, projectId, env, yamlKey, oldYaml, yamlSource);

            managerRepository.update(profile, configMap);
        } else {
            Map<String, String> configs = KeyValueUtil.getKVConfigs(configMap);

            Map<String, Object> map = managerRepository.get(profile, Map.class);
            if (configMap==null || map==null) {
                return;
            }
            for (Map.Entry<String, String> entry: configs.entrySet()) {
                String oldValue = KeyValueUtil.change(map, entry.getKey(), entry.getValue());
                //保存历史记录
                this.saveKeyValueRecord(userId, projectId, env, entry.getKey(), oldValue, entry.getValue());
            }
            managerRepository.update(profile, map);
        }
    }

    /**
     * 获取YAML配置格式
     */
    public String getYaml(String projectId, String env) {
        String profile = this.keyValueProfile(projectId, env, profileSlash);
        return managerRepository.getYaml(profile);
    }

    /**
     * KeyValue配置YAML格式化
     */
    public void formatYaml(String projectId, String env) {
        String profile = this.keyValueProfile(projectId, env, profileSlash);
        Map<String, Object> map = managerRepository.get(profile, Map.class);
        if (map==null) {
            return;
        }
        Map<String, String> configs = KeyValueUtil.getKVConfigs(map);
        Map<String, Object> result = new LinkedHashMap<>();
        for (Map.Entry<String, String> entry: configs.entrySet()) {
            KeyValueUtil.change(result, entry.getKey(), entry.getValue());
        }
        managerRepository.update(profile, result);
    }

    /**
     * 获取历史记录
     */
    public List<KeyValueRecord> getKeyValueRecords(String projectId, String env, String key) {
        String profile = String.format(StringUtils.isEmpty(key)? recordPrefixFormat: recordFormat, this.keyValueProfile(projectId, env, profileDot), key, "");
        List<KeyValueRecord> records = managerRepository.getList(profile, KeyValueRecord.class);
        //按时间倒序
        records.sort(Comparator.comparing(KeyValueRecord::getCreatedDate).reversed());
        return records.size()>100? records.subList(0, 100): records;
    }

    /**
     * 保存历史记录
     */
    public void saveKeyValueRecord(String userId, String projectId, String env, String key, String oldValue, String newValue) {
        Date currDate = new Date();
        String ext = DateUtil.formatDate(currDate, pattern);
        String profile = String.format(recordFormat, this.keyValueProfile(projectId, env, profileDot), key, ext);

        KeyValueRecord record = new KeyValueRecord(key, oldValue, newValue, userId, currDate);
        managerRepository.update(profile, record);
    }

    /**
     * 获取keyValue配置文件Profile
     */
    private String keyValueProfile(String projectId, String env, String slash) {
        //默认配置不加环境变量
        if (StringUtils.isEmpty(env) || "def".equals(env.toLowerCase())) {
            return keyValuePrefix + slash + projectId + slash + keyValueDataKey;
        }
        return keyValuePrefix + slash + projectId + profileSeparator + env + slash + keyValueDataKey;
    }
}
